package com.wcs.exam.common.server.upload.impl;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.StrUtil;
import com.qcloud.cos.COSClient;
import com.qcloud.cos.ClientConfig;
import com.qcloud.cos.auth.BasicCOSCredentials;
import com.qcloud.cos.auth.COSCredentials;
import com.qcloud.cos.http.HttpProtocol;
import com.qcloud.cos.model.*;
import com.qcloud.cos.model.ciModel.job.DocHtmlRequest;
import com.qcloud.cos.region.Region;
import com.wcs.exam.common.server.upload.Upload;
import com.wcs.exam.common.server.upload.UploadFace;
import com.wcs.exam.common.util.FileUtils;
import com.wcs.exam.common.util.ImgUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;

import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.net.URISyntaxException;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.*;

/**
 * 腾讯云 COS
 */
@Slf4j
@Component(value = "tencent")
public class TencentUploadImpl implements UploadFace {

    @Override
    public String uploadPic(MultipartFile file, Upload upload) {
        return putObject(file, upload, CannedAccessControlList.PublicRead);
    }

    @Override
    public String uploadPicBase64(String imgBase64, Upload upload) {
        // 获取图片类型
        String type = ImgUtil.getBase64Type(imgBase64);
        if (StrUtil.isBlank(type)) {
            type = "jpg";
        }
        return uploadPicBase64(imgBase64, FileUtils.getFileName(type), upload);
    }

    @Override
    public String uploadPicBase64(String imgBase64, String fileName, Upload upload) {
        // 解码Base64字符串
        byte[] data = Base64.getDecoder().decode(imgBase64);
        //  创建字节数组输入流
        InputStream inputStream = new ByteArrayInputStream(data);
        // 上传文件
        return putObject(inputStream, fileName, upload, CannedAccessControlList.PublicRead);
    }

    @Override
    public String uploadDoc(MultipartFile file, Upload upload, Boolean isPublicRead) {
        return putObject(file, upload, isPublicRead ? CannedAccessControlList.PublicRead : CannedAccessControlList.Private);
    }

    @Override
    public String uploadDoc(File file, String fileName, Upload upload, Boolean isPublicRead) {
        return putObject(file, fileName, upload, isPublicRead ? CannedAccessControlList.PublicRead : CannedAccessControlList.Private);
    }

    @Override
    public void delete(String fileUrl, Upload upload) {
        String key = fileUrl.replace(upload.getTencentCosUrl(), "");
        getCOSClient(upload).deleteObject(upload.getTencentCosBucket(), key);
    }

    @Override
    public void delete(Collection<String> fileUrls, Upload upload) {
        List<DeleteObjectsRequest.KeyVersion> keyList = new ArrayList<>();
        for (String url : fileUrls) {
            keyList.add(new DeleteObjectsRequest.KeyVersion(url.replace(upload.getTencentCosUrl(), "")));
            if (keyList.size() >= 1000) {
                batchDeleteObject(upload, keyList);
                keyList.clear();
            }
        }
        if (CollectionUtil.isNotEmpty(keyList)) {
            batchDeleteObject(upload, keyList);
        }
    }

    @Override
    public String getDownloadUrl(String docUrl, int expireSeconds, Upload upload) {
        GeneratePresignedUrlRequest req = new GeneratePresignedUrlRequest(upload.getTencentCosBucket(), docUrl.replace(upload.getTencentCosUrl(), ""));
        req.setExpiration(new Date(System.currentTimeMillis() + expireSeconds * 1000L));
        return docUrl + "?" + getCOSClient(upload).generatePresignedUrl(req).getQuery();
    }

    @Override
    public String getPreviewConfig(String docUrl, int expireSeconds, Upload upload) {
        //1.创建请求对象
        DocHtmlRequest request = new DocHtmlRequest();
        //2.添加请求参数，参数详情请见 API 接口文档
        request.setBucketName(upload.getTencentCosBucket());
        //如果需要转为图片 dstType 为 DocHtmlRequest.DocType.jpg
        request.setDstType(DocHtmlRequest.DocType.html);
        request.setObjectKey(docUrl.replace(upload.getTencentCosUrl(), ""));
        request.setPage("1");
        try {
            //3.调用接口，获取任务响应对象
            return getCOSClient(upload).GenerateDocPreviewUrl(request);
        } catch (URISyntaxException e) {
            log.error("获取文件预览链接出错", e);
            return "";
        }
    }

    /**
     * 存入OSS
     */
    private static String putObject(File file, String fileName, Upload upload, CannedAccessControlList acl) {
        try (InputStream inputStream = new FileInputStream(file)) {
            return putObject(inputStream, fileName, upload, acl);
        } catch (Exception e) {
            log.error("上传失败", e);
            return "";
        }
    }

    /**
     * 存入OSS
     */
    private static String putObject(MultipartFile file, Upload upload, CannedAccessControlList acl) {
        try (InputStream inputStream = file.getInputStream()) {
            String name = file.getOriginalFilename();
            return putObject(inputStream, name, upload, acl);
        } catch (Exception e) {
            log.error("上传失败", e);
            return "";
        }
    }


    /**
     * 上传文件
     */
    private static String putObject(InputStream inputStream, String fileName, Upload upload, CannedAccessControlList cannedAccessControlList) {
        try {
            String filePath = FileUtils.getFilePath(FileUtils.PREFIX, FileUtils.getFileName(fileName.substring(fileName.lastIndexOf(".") + 1)));
            ObjectMetadata meta = new ObjectMetadata();
            meta.setContentDisposition("attachment;filename={}".replace("{}", URLEncoder.encode(fileName, StandardCharsets.UTF_8)));
            PutObjectRequest putObjectRequest = new PutObjectRequest(upload.getTencentCosBucket(), filePath, inputStream, meta);
            putObjectRequest.setCannedAcl(cannedAccessControlList);
            getCOSClient(upload).putObject(putObjectRequest);
            return FileUtils.getFilePath(upload.getTencentCosUrl(), filePath);
        } catch (Exception e) {
            log.error("上传失败", e);
            return "";
        }
    }

    /**
     * 删除Bucket中的Object
     */
    private static void batchDeleteObject(Upload upload, List<DeleteObjectsRequest.KeyVersion> keyList) {
        DeleteObjectsRequest deleteObjectsRequest = new DeleteObjectsRequest(upload.getTencentCosBucket());
        deleteObjectsRequest.setKeys(keyList);
        getCOSClient(upload).deleteObjects(deleteObjectsRequest);
    }

    private static COSClient getCOSClient(Upload storageConfig) {
        String secretId = storageConfig.getTencentCosSecretId();
        String secretKey = storageConfig.getTencentCosSecretKey();
        COSCredentials cred = new BasicCOSCredentials(secretId, secretKey);
        Region region = new Region(storageConfig.getTencentCosRegion());
        ClientConfig clientConfig = new ClientConfig(region);
        clientConfig.setHttpProtocol(HttpProtocol.https);
        COSClient cosClient = new COSClient(cred, clientConfig);
        if (!cosClient.doesBucketExist(storageConfig.getTencentCosBucket())) {
            CreateBucketRequest createBucketRequest = new CreateBucketRequest(storageConfig.getTencentCosBucket());
            // 设置 bucket 的权限为 Private(私有读写)、其他可选有 PublicRead（公有读私有写）、PublicReadWrite（公有读写）
            createBucketRequest.setCannedAcl(CannedAccessControlList.Private);
            cosClient.createBucket(createBucketRequest);
            try {
                // 当前线程暂停10s，等待bucket创建完成
                Thread.sleep(10000);
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
        }
        return cosClient;
    }
}
